// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved.
// Roland Pheasant licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using DynamicData.Annotations;

// ReSharper disable once CheckNamespace
namespace DynamicData
{
    /// <summary>
    /// A set of changes which has occured since the last reported change
    /// </summary>
    /// <typeparam name="T">The type of the object.</typeparam>
    public class ChangeSet<T> : List<Change<T>>, IChangeSet<T>
    {
        /// <summary>
        /// An empty change set
        /// </summary>
        public static readonly IChangeSet<T> Empty = new ChangeSet<T>();

        /// <summary>
        /// Initializes a new instance of the <see cref="ChangeSet{T}"/> class.
        /// </summary>
        public ChangeSet()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ChangeSet{T}" /> class.
        /// </summary>
        /// <param name="items">The items.</param>
        /// <exception cref="System.ArgumentNullException">items</exception>
        public ChangeSet([NotNull] IEnumerable<Change<T>> items)
            : base(items)
        {
        }

        /// <summary>
        ///     Gets the number of additions
        /// </summary>
        public int Adds
        {
            get
            {
                int adds = 0;
                foreach (var item in this)
                {
                    switch (item.Reason)
                    {
                        case ListChangeReason.Add:
                            adds++;
                            break;
                        case ListChangeReason.AddRange:
                            adds += item.Range.Count;
                            break;
                    }
                }
                return adds;
            }
        }

        /// <summary>
        ///     Gets the number of updates
        /// </summary>
        public int Replaced => this.Count(c => c.Reason == ListChangeReason.Replace);

        /// <summary>
        ///     Gets the number of removes
        /// </summary>
        public int Removes
        {
            get
            {
                int removes = 0;
                foreach (var item in this)
                {
                    switch (item.Reason)
                    {
                        case ListChangeReason.Remove:
                            removes++;
                            break;
                        case ListChangeReason.RemoveRange:
                        case ListChangeReason.Clear:
                            removes += item.Range.Count;
                            break;
                    }
                }
                return removes;
            }
        }

        /// <summary>
        ///     Gets the number of removes
        /// </summary>
        public int Refreshes => this.Count(c => c.Reason == ListChangeReason.Refresh);

        /// <summary>
        ///     Gets the number of moves
        /// </summary>
        public int Moves => this.Count(c => c.Reason == ListChangeReason.Moved);

        /// <summary>
        ///     The total number if individual item changes
        /// </summary>
        public int TotalChanges => Adds + Removes + Replaced + Moves;

        /// <summary>
        /// Returns a <see cref="System.String" /> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String" /> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return $"ChangeSet<{typeof(T).Name}>. Count={Count}";
        }
    }
}
